// ========================================================================
// Copyright (c) 2022 ChangYou, All rights reserved.
// http://www.changyou.com/
// 
// Filename:    RenderingQueueMeshInstanceSimulator.cs
// Time:        2022-08-22 21:11:35
// Author:      xiangjinbao
// Email:       xiangjinbao@cyou-inc.com
// Version:     v2022.0.1
// Description: RenderingQueueMeshInstanceSimulator
// ========================================================================

using System.Collections.Generic;
using UnityEngine;

namespace ProjectTW.Foliage
{
    /// <summary>
    /// An imposter struct to simulate an instance.
    /// </summary>
    public struct RenderingQueueMeshInstanceSimulator
    {
        public static bool SettingsIsPlaying = false;
        public static bool SettingsUseQualitySettingsShadows = false;
        public static float SettingsShadowsDistance = 0;
        public static bool CullingDisalbed = false;

        private static Vector3 ReusableVector3Instance = new Vector3();
        private static Vector4 ReusableVector4Instance = new Vector4();

        public float x, z;
        public float worldX, worldZ;

        [SerializeField]
        private FoliageMeshInstance m_MeshInstance;
        [SerializeField]
        private byte m_Density;
        [SerializeField]
        private Mesh m_Mesh;

        private GPUInstancingStack m_InstancingStack;
        private int m_LodIndex;
        private float m_DistanceToCenter;

        public byte Density
        {
            get
            {
                return m_Density;
            }
        }

        public Mesh Mesh
        {
            get
            {
                return m_Mesh;
            }
        }

        public GPUInstancingStack InstancingStack
        {
            get
            {
                return m_InstancingStack;
            }
        }

        public RenderingQueueMeshInstanceSimulator(Vector3 position, FoliageMeshInstance meshInstance, FoliageInstanceObject mInstance, FoliagePrototype prototype,
                                                   Vector4 worldCameraPosition)
        {
            x = position.x;
            z = position.z;
            worldX = x + mInstance.m_Pos.x;
            worldZ = z + mInstance.m_Pos.z;
            m_MeshInstance = meshInstance;
            m_Mesh = null;
            m_Density = 0;
            m_InstancingStack = null;
            m_LodIndex = 0;
            m_DistanceToCenter = m_MeshInstance.DistanceToCenter; // assign first before using the method.
        }

        public RenderingQueueMeshInstanceSimulator UpdateDensity(FoliageGPUMesh foliageGpuMesh, byte maxDensity, FoliageInstanceObject foliageInstance, int mapWidth, float densityMultiplier,
                                                                 FoliagePrototype prototype, Dictionary<Mesh, GPUInstancingStack> stackInstances)
        {
            byte newDensity = m_MeshInstance.GetDensity(x, z, maxDensity, foliageInstance.DensityMap, mapWidth);
            m_LodIndex = 0;
            float lodMultiplier = prototype.GetLODMultiplier(m_DistanceToCenter, ref m_LodIndex);
            int interpolatedDensity = (int) (newDensity * densityMultiplier);

            if (interpolatedDensity > 0)
            {
                interpolatedDensity = Mathf.CeilToInt(interpolatedDensity * lodMultiplier);
            }

            m_Density = newDensity;

            if (m_Density == 0)
            {
                return this;
            }

            int meshLODIndex = foliageGpuMesh.GetMesh(interpolatedDensity, prototype);

            if (meshLODIndex == -1)
            {
                m_Mesh = null;

                return this;
            }

            if (foliageGpuMesh.MeshLODsCount == 1) // 0 lods
            {
                m_Mesh = foliageGpuMesh.MeshesCache[0, meshLODIndex, m_MeshInstance.MeshIndex];
            }
            else
            {
                m_Mesh = foliageGpuMesh.MeshesCache[m_LodIndex, meshLODIndex, m_MeshInstance.MeshIndex];
            }

            if (stackInstances != null)
            {
                m_InstancingStack = stackInstances[m_Mesh];
            }

            return this;
        }

        public void Render(MaterialPropertyBlock propertyBlock, Camera camera, Plane[] cameraPlanes, Vector3 normalizedCameraPosition, bool instancing)
        {
            ReusableVector3Instance.x = worldX;
            ReusableVector3Instance.z = worldZ;

            if (m_Density == 0 || (!CullingDisalbed && !m_MeshInstance.CheckViewPort(cameraPlanes, ReusableVector3Instance, normalizedCameraPosition.y)))
            {
                return;
            }

            ReusableVector4Instance.x = x;
            ReusableVector4Instance.z = z;

            // 添加到渲染队列
            if (instancing && m_InstancingStack != null)
            {
                m_InstancingStack.Add(FoliageMeshInstance.GenerationOptimizationPreGenerationMatrixIdentity, ReusableVector4Instance); // add instance to stack.
            }
            else // 立即绘制
            {
                propertyBlock.SetVector(FoliageMeshManager.PropertyIdWorldposition, ReusableVector4Instance);
                m_MeshInstance.DrawAndUpdate(ReusableVector4Instance, m_Mesh, SettingsIsPlaying ? camera : null, normalizedCameraPosition, propertyBlock,
                        SettingsUseQualitySettingsShadows, SettingsShadowsDistance);
            }
        }
    }
}
